/*
 * BoardInfos.cpp
 * Author: Elias Capito, Yanick Candaux
 * PCM
 */


#include "BoardInfos.hpp"
#include <cstring>

namespace pcm {

LL BoardInfos::cons = 0;

short int BoardInfos::m_gameNumberHole = 0;
short int BoardInfos::m_gameSize = 0;

 HolePosition* BoardInfos::m_holePosition = NULL;

vector<HoleJump>*  BoardInfos::m_startHoleJumps = NULL;
size_t *BoardInfos::m_startHoleJumpsSize = NULL;

vector<HoleJump>*  BoardInfos::m_endHoleJumps = NULL;
size_t *BoardInfos::m_endHoleJumpsSize = NULL;

vector<HolePermutation> *BoardInfos::m_holesPermutation = NULL;
size_t *BoardInfos::m_holesPermutationSize = NULL;

BoardInfos::BoardInfos() {
#ifdef CACLUTATE_CONS_AND_DES
	#pragma omp atomic
	cons++;
#endif

	m_board=0;
	m_numberPeg=0;
	m_numberJoin=0;
}

BoardInfos::~BoardInfos() {
#ifdef CACLUTATE_CONS_AND_DES
		#pragma omp atomic
		cons--;
#endif
}


void BoardInfos::applyHolesPermutations(){
	LL actualBoard= m_board;

	//CW60
	vector<HolePermutation>& CW60 = m_holesPermutation[ECW60];
	LL tmpCW60 = actualBoard;
	size_t size = m_holesPermutationSize[ECW60];
	for(size_t i=0;i<size;i++){
		copyHole(tmpCW60,actualBoard, CW60[i].start(),CW60[i].end());
	}

	//Check if rotation is same board
	if(tmpCW60!=m_board){

		if(tmpCW60<m_board){
			m_board = tmpCW60;
		}

		//Check reverse to see if is symetric
		vector<HolePermutation>& reverse = m_holesPermutation[EReverse];
		LL tmpReverse = actualBoard;
		size = m_holesPermutationSize[EReverse];
		for(size_t i=0;i<size;i++){
			swap(tmpReverse,reverse[i].start(),reverse[i].end());
		}

		if(actualBoard!=tmpReverse){

			if(tmpReverse<m_board){
				m_board = tmpReverse;
			}

			//Reverse CW60
			vector<HolePermutation>& reverseCW60 = m_holesPermutation[EReverseCW60];
			LL tmpReverseCW60 = actualBoard;
			size = m_holesPermutationSize[EReverseCW60];
			for(size_t i=0;i<size;i++){
				copyHole(tmpReverseCW60,actualBoard, reverseCW60[i].start(),reverseCW60[i].end());
			}
			if(tmpReverseCW60<m_board){
				m_board = tmpReverseCW60;
			}
			//Reverse CCW60
			vector<HolePermutation>& reverseCCW60 = m_holesPermutation[EReverseCCW60];
			LL tmpReverseCCW60 = actualBoard;
			size = m_holesPermutationSize[EReverseCCW60];
			for(size_t i=0;i<size;i++){
				copyHole(tmpReverseCCW60,actualBoard, reverseCCW60[i].start(),reverseCCW60[i].end());
			}
			if(tmpReverseCCW60<m_board){
				m_board = tmpReverseCCW60;
			}
		}

		//CCW60
		vector<HolePermutation>& CCW60 = m_holesPermutation[ECCW60];
		LL tmpCCW60 = actualBoard;
		size = m_holesPermutationSize[ECCW60];
		for(size_t i=0;i<size;i++){
			copyHole(tmpCCW60,actualBoard, CCW60[i].start(),CCW60[i].end());
		}
		if(tmpCCW60<m_board){
			m_board = tmpCCW60;
		}
	}
}


/**
 * Fill board with a peg in each hole
 */
void BoardInfos::fillBoard(){
	m_numberPeg = gameNumberHole();
	for(short int hole=0;hole<gameNumberHole();hole++){
		setHole(hole,true);
	}
}

/**
 * Update game infos in relation of game size (gamesize, numberhole, holeposition,
 * holejumps, rotates and sym)
 * @param gameSize
 */
void BoardInfos::updateGameInfos(short int gameSize){
	setGameSize(gameSize);
	setGameNumberHole(gameSize);
	calculateHolesPos();
	calculateHolesJumps();
	calculateRotatesAndSymmetries();

}

/**
 * Print a board
 * @param _board
 * @param _size
 * @param _name
 */
void BoardInfos::printBoard(LL _board,short int _size, string _name){
	cout << _name<<"Board size:"<<_size<<" val:"<<_board<<endl;
	for(short int lign=0;lign<_size;lign++){
		cout.width(_size-lign);
		for(short int col=0;col<=lign;col++){
			cout << hole(_board,lign,col)<< " ";
		}
		cout <<endl;
	}
}
/**
 * Print object board
 */
void BoardInfos::printBoard() const{
	cout << "Board size:"<<gameSize()<< "#hole:"<<gameNumberHole()<<" #peg:"<<numberPeg()<<" #join:"<<numberJoin()<<endl;
	for(short int lign=0;lign<gameSize();lign++){
		cout.width(gameSize()-lign);
		for(short int col=0;col<=lign;col++){
			cout << hole(lign,col)<< " ";
		}
		cout <<endl;
	}
}
/**
 * Print calculated jumps
 */
void BoardInfos::printJumps() const{
	cout << "Holes jumps:"<<endl;
	short int i = 0;
	cout <<"Start:"<<endl;
	for(short int iStartHole=0;iStartHole<gameNumberHole();iStartHole++){
		vector<HoleJump> jumps = startHoleJumps(iStartHole);
		for(size_t iJumps=0;iJumps<jumps.size();iJumps++){
			cout.width(4);
			cout <<i;
			cout<<": "<< jumps.at(iJumps)<<endl;
			i++;
		}
	}
	i=0;
	cout << "End:"<<endl;
	for(short int iEndHole=0;iEndHole<gameNumberHole();iEndHole++){
		vector<HoleJump> jumps = endlHoleJumps(iEndHole);
		for(size_t iJumps=0;iJumps<jumps.size();iJumps++){
			cout.width(4);
			cout <<i;
			cout<<": "<< jumps.at(iJumps)<<endl;
			i++;
		}
	}
}
/**
 * Print holes position
 */
void BoardInfos::printHolePos() const{
	cout <<"Holes position:"<<endl;
	for(short int iHole=0;iHole<gameNumberHole();iHole++){
		cout << holePosition(iHole)<<endl;
	}
}

/**
 * Calculate holes position
 */
void BoardInfos::calculateHolesPos(){
	if(m_holePosition!=NULL){
		delete[] m_holePosition;
	}
	m_holePosition = new HolePosition[gameNumberHole()];
	short int nHole;
	for(short int lign=0;lign<gameSize();lign++){
		for(short int col=0;col<=lign;col++){
			nHole = HolePosition::convToHoleNumber(lign,col);
			m_holePosition[nHole].updatePost(lign,col);
		}
	}
}

/**
 * Calculate holes jumps
 */
void BoardInfos::calculateHolesJumps(){

	if(m_startHoleJumps != NULL){
		delete[] m_startHoleJumps;
	}
	m_startHoleJumps = new vector<HoleJump>[gameNumberHole()];
	if(m_startHoleJumpsSize != NULL){
		delete[] m_startHoleJumpsSize;
	}
	m_startHoleJumpsSize = new size_t[gameNumberHole()];

	if(m_endHoleJumps != NULL){
		delete[] m_endHoleJumps;
	}
	m_endHoleJumps = new vector<HoleJump>[gameNumberHole()];
	if(m_endHoleJumpsSize != NULL){
		delete[] m_endHoleJumpsSize;
	}
	m_endHoleJumpsSize = new size_t[gameNumberHole()];

	#pragma omp for
	for(short int iHole=0;iHole<gameNumberHole();iHole++){
		const HolePosition& holePos = holePosition(iHole);
		vector<HoleJump> jumps;

		//Check 6 directions
		if(holePos.lign()>1){
			if(holePos.col()<holePos.lign()-1){
				//NE (l-1, c),(l-2, c)
				HoleJump ne(iHole,
							HolePosition::convToHoleNumber(holePos.lign()-1,holePos.col()),
							HolePosition::convToHoleNumber(holePos.lign()-2,holePos.col()));
				m_startHoleJumps[iHole].push_back(ne);
				m_endHoleJumps[ne.end()].push_back(ne);
				//E (l,c+1),(l,c+2)
				HoleJump e( iHole,
							HolePosition::convToHoleNumber(holePos.lign(),holePos.col()+1),
							HolePosition::convToHoleNumber(holePos.lign(),holePos.col()+2));
				m_startHoleJumps[iHole].push_back(e);
				m_endHoleJumps[e.end()].push_back(e);
			}
			if(holePos.col()>1){
				//NW (l-1,c-1),(l-2,c-2)
				HoleJump nw(iHole,
							HolePosition::convToHoleNumber(holePos.lign()-1,holePos.col()-1),
							HolePosition::convToHoleNumber(holePos.lign()-2,holePos.col()-2));
				m_startHoleJumps[iHole].push_back(nw);
				m_endHoleJumps[nw.end()].push_back(nw);
				//W	(l,c-1),(l,c-2)
				HoleJump w( iHole,
							HolePosition::convToHoleNumber(holePos.lign(),holePos.col()-1),
							HolePosition::convToHoleNumber(holePos.lign(),holePos.col()-2));
				m_startHoleJumps[iHole].push_back(w);
				m_endHoleJumps[w.end()].push_back(w);
			}
		}
		if(holePos.lign()<gameSize()-2){
			//SE (l+1,c+1),(l+2,c+2)
			if(holePos.col()<gameSize()-2){
				HoleJump se(iHole,
							HolePosition::convToHoleNumber(holePos.lign()+1,holePos.col()+1),
							HolePosition::convToHoleNumber(holePos.lign()+2,holePos.col()+2));
				m_startHoleJumps[iHole].push_back(se);
				m_endHoleJumps[se.end()].push_back(se);
			}
			//SW (l+1,c),(l+2,c)
			HoleJump sw(iHole,
						HolePosition::convToHoleNumber(holePos.lign()+1,holePos.col()),
						HolePosition::convToHoleNumber(holePos.lign()+2,holePos.col()));
			m_startHoleJumps[iHole].push_back(sw);
			m_endHoleJumps[sw.end()].push_back(sw);
		}
	}
	for(short int iHole=0;iHole<gameNumberHole();iHole++){
		m_startHoleJumpsSize[iHole] = m_startHoleJumps[iHole].size();
		m_endHoleJumpsSize[iHole] = m_endHoleJumps[iHole].size();
	}
}

/**
 * Calculate rotates and symmetries
 */
void BoardInfos::calculateRotatesAndSymmetries(){
	//Initialization
	if(m_holesPermutation != NULL){
		delete[] m_holesPermutation;
	}
	if(m_holesPermutationSize != NULL){
		delete[] m_holesPermutationSize;
	}
	m_holesPermutation = new vector<HolePermutation>[5];
	m_holesPermutationSize = new size_t[5];

	//Permutation for reverse
	vector<HolePermutation> reverse;
	for(short int lign= 0;lign<gameSize();lign++){
		for(short int col= 0;col<(lign+1)/2;col++){
			reverse.push_back(HolePermutation(
					HolePosition::convToHoleNumber(lign,col),
					HolePosition::convToHoleNumber(lign,lign-col),true));
		}
	}
	m_holesPermutationSize[EReverse] = reverse.size();
	m_holesPermutation[EReverse] = reverse;

	//Permutation for CW60
	short int iHolecw60 = 0;
	vector<HolePermutation> cw60;
	for(short int i=0;i<gameSize();i++){
		for(short int j=i;j>=0;j--){
			cw60.push_back(HolePermutation(iHolecw60,HolePosition::convToHoleNumber(gameSize()+j-i-1,j)));
			iHolecw60++;
		}
	}
	m_holesPermutationSize[ECW60] = cw60.size();
	m_holesPermutation[ECW60] = cw60;

	//Permutation for CCW60
	short int iHoleccw60 = 0;
	vector<HolePermutation> ccw60;
	for(short int i=gameSize()-1;i>=0;i--){
		for(short int j=i;j<gameSize();j++){
			ccw60.push_back(HolePermutation(iHoleccw60,HolePosition::convToHoleNumber(j,i)));
			iHoleccw60++;
		}
	}
	m_holesPermutationSize[ECCW60] = ccw60.size();
	m_holesPermutation[ECCW60] = ccw60;

	//Permutation for reverse CW60
	short int iHoleReverseCW60 = 0;
	vector<HolePermutation> reverseCW60;
	for(short int i=0;i<gameSize();i++){
		for(short int j=0;j<=i;j++){
			reverseCW60.push_back(HolePermutation(iHoleReverseCW60,HolePosition::convToHoleNumber(gameSize()+j-i-1,j)));
			iHoleReverseCW60++;
		}
	}
	m_holesPermutationSize[EReverseCW60] = reverseCW60.size();
	m_holesPermutation[EReverseCW60] = reverseCW60;

	//Permutation for reverse CCW60
	short int iHoleReverseCCW60 = 0;
	vector<HolePermutation> reverseCCW60;
	for(short int i=gameSize()-1;i>=0;i--){
		for(short int j=(gameSize()-1);j>=i;j--){
			reverseCCW60.push_back(HolePermutation(iHoleReverseCCW60,HolePosition::convToHoleNumber(j,i)));
			iHoleReverseCCW60++;
		}
	}
	m_holesPermutationSize[EReverseCCW60] = reverseCCW60.size();
	m_holesPermutation[EReverseCCW60] = reverseCCW60;
}

void BoardInfos::initBoard(short int holeToRemove){
	m_numberJoin = 0;
	fillBoard();
	setHole(holeToRemove,false);
	decrementNumberPeg();
	incrementNumberJoin();
}

} /* namespace pcm */
